import { SnapObject } from "./object.js";
import { Color } from "./color.js";

function lerp(s, e, a) {
    return (e - s) * a + s;
}

function getSprite(stage, sprite) {
    if (typeof sprite === "string") {
        return stage.getSprite(sprite);
    }

    return sprite;
}

class Sprite extends SnapObject {
    constructor(stage) {
        super(stage);

        this.varScope.prefix = "env.locals.";
        this.stage = stage;
        this.name = "Sprite";
        this.bubble = null;

        this.x = 0;
        this.y = 0;
        this.scale = 1;
        this.direction = 0;

        this.penSize = 1;
        this.penDown = false; // false
        this.color = new Color(80, 80, 80);

        var sprite = this;

        this.scriptEnv.broadcast = function(msg) {
            sprite.stage.broadcast("message", msg);
        }

        this.scriptEnv.broadcastWait = function*(msg) {
            yield* sprite.stage.broadcastSync(msg);
        }

        this.scriptEnv.penClear = function() {
            sprite.stage.clear();
        }

        this.scriptEnv.sprite = {
            bubble(text) {
                sprite.setBubble(text);
            },

            *sayFor(text, sec) {
                sprite.setBubble(text);
                yield 1000*(+sec);
                sprite.setBubble();
            },

            forward(n) {
                n = +n;
                sprite.x += Math.cos(sprite.direction) * n;
                sprite.y += Math.sin(sprite.direction) * n
            },

            turn(deg) {
                sprite.direction -= +deg / 180 * Math.PI;
            },

            turnLeft(deg) {
                sprite.direction += +deg / 180 * Math.PI;
            },

            setHeading(deg) {
                sprite.direction = -(+deg - 90) / 180 * Math.PI;
            },

            faceTowards(obj) {
                var target = getSprite(sprite.stage, obj);
                sprite.direction = Math.atan2(target.y - sprite.y, target.x - sprite.x);
            },

            goto(obj) {
                if (Array.isArray(obj)) {
                    return sprite.goto(obj[0], obj[1]);
                }

                var other = getSprite(sprite.stage, obj);

                sprite.goto(other.x, other.y);
            },

            gotoXY(x, y) {
                sprite.goto(+x, +y);
            },

            setX(x) {
                sprite.goto(+x, sprite.y);
            },

            setY(y) {
                sprite.goto(sprite.x, +y);
            },

            changeX(dx) {
                sprite.goto(sprite.x + +dx, sprite.y);
            },

            changeY(dy) {
                sprite.goto(sprite.x, sprite.y + +dy);
            },

            *glide(t, x, y) {
                t = +t;
                x = +x;
                y = +y;

                var startX = sprite.x;
                var startY = sprite.y;
                var elapsed = 0;

                while (elapsed < t) {
                    let a = elapsed / t;
                    sprite.goto(lerp(startX, x, a), lerp(startY, y, a));
                    elapsed += yield 0;
                }

                sprite.goto(x, y);
            },

            setScale(n) {
                sprite.scale = +n / 100;
            },

            changeScale(n) {
                sprite.scale += +n / 100;
            },

            getScale() {
                return sprite.scale * 100;
            },

            penDown() {
                sprite.penDown = true;
            },

            penUp() {
                sprite.penDown = false;
            },

            setPenSize(n) {
                sprite.penSize = +n;
            },
        }
    }

    async loadXml(xml, shared) {
        if (xml.nodeName !== "sprite") throw new Error("Expected <sprite> tag");
        await super.loadXml(xml, shared);

        var attrib = (n) => xml.attributes[n].nodeValue;

        this.x = attrib("x");
        this.y = attrib("y");
        this.direction = -(attrib("heading") - 90) / 180 * Math.PI;
    }

    getProject() {
        return this.stage.getProject();
    }

    get scene() {
        return this.stage.scene;
    }

    setBubble(msg) {
        console.log(this.name + " says", msg);
        this.bubble = msg;
    }

    goto(x, y) {
        if (this.penDown) {
            this.stage.drawLine(this.x, this.y, x, y, this.color, this.penSize);
        }

        this.x = x;
        this.y = y;
    }

    render(canvas, ctx) {
        if (!this.shown) return;

        ctx.fillStyle = "black";

        ctx.save();
        ctx.translate(this.x, -this.y);
        ctx.rotate(this.direction);
        this.applyEffects(ctx);
        
        if (this.curCostume) {
            ctx.scale(this.scale, this.scale);
            ctx.drawImage(this.curCostume.canvas, this.curCostume.width / -2, this.curCostume.height / -2);
        } else {
            ctx.fillStyle = this.color.toRGBString();
            
            // draw and fill turtle shape
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(-40 * this.scale, -10 * this.scale);
            ctx.lineTo(-30 * this.scale, 0);
            ctx.lineTo(-40 * this.scale, 10 * this.scale);
            ctx.lineTo(0, 0);
            ctx.fill();
            
            // draw inner darker outline
            ctx.lineWidth = 2;
            ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
            ctx.stroke();

            // draw white outline
            ctx.lineWidth = 1;
            ctx.strokeStyle = "white";
            ctx.stroke();

            ctx.closePath();
        }

        ctx.restore();
    }
 }

 export { Sprite }